using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace GarbageTruck.TemporalExpressions
{
    public struct DateUnits
    {
        public const string Day = "d";
        public const string Week = "w";
        public const string Month = "m";
        public const string Year = "y";
    }

    public class DateUnit : ITemporalExpression
    {

        #region CONSTRUCTORS

        public DateUnit()
        {
        }

        public DateUnit(string expresion)
        {
            string[] parameters = expresion.Split(',');

            this.Number = int.Parse(parameters[0]);
            this.Unit = parameters[1];
            this.Start = DateTime.Parse(parameters[2]).Date;
            this.Amount = TimeSpan.Parse(parameters[3]);
        }

        public DateUnit(int number, string unit, DateTime start, TimeSpan amount)
        {
            this.Number = number;
            this.Unit = unit;
            this.Start = start;
            this.Amount = amount;
        }

        #endregion CONSTRUCTORS

        #region FIELDS

        private int m_Number;
        private string m_Unit;
        private DateTime m_Start;
        private TimeSpan m_Amount;

        #endregion FIELDS

        #region PROPERTIES

        public int Number
        {
            get { return m_Number; }
            set { m_Number = value; }
        }

        public string Unit
        {
            get { return m_Unit; }
            set { m_Unit = value; }
        }

        public DateTime Start
        {
            get { return m_Start; }
            set { m_Start = value; }
        }

        public TimeSpan Amount
        {
            get { return m_Amount; }
            set { m_Amount = value; }
        }


        #endregion PROPERTIES

        #region METHODS


        public bool Contains(DateTime dateTime)
        {
            long ticks = 0;
            int value = 0;
            TimeSpan timeSpam = new TimeSpan();
            DateTime startOcurrence = new DateTime();
            DateTime endOcurrence = new DateTime();

            if (dateTime >= this.Start && dateTime <= this.Start.AddTicks(this.Amount.Ticks)) return true;
            if (dateTime < Start) return false;

            switch (this.Unit)
            {
                case DateUnits.Year:
                    value = dateTime.Year - Start.Year;
                    value = int.Parse(Math.Floor((double)(value / Number)).ToString());
                    startOcurrence = new DateTime(Start.Year + Number * value, Start.Month, Start.Day);
                    break;

                case DateUnits.Month:
                    value = (12 * (dateTime.Year - Start.Year)) + dateTime.Month - Start.Month;
                    value = int.Parse(Math.Floor((double)(value / Number)).ToString());
                    int monthYears = int.Parse(Math.Floor((double)(Number * value)).ToString());
                    startOcurrence = new DateTime(Start.Year + monthYears, (Start.Month + Number * value) - monthYears, Start.Day);
                    break;

                case DateUnits.Week:
                    timeSpam = dateTime.Date.Subtract(Start);
                    ticks = long.Parse(Math.Floor((decimal)(timeSpam.Ticks / TimeSpan.FromDays(Number).Ticks)).ToString())/7;
                    startOcurrence = Start.Add(TimeSpan.FromTicks(ticks));
                    value = ((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)startOcurrence.DayOfWeek);
                    startOcurrence = startOcurrence.AddDays(value);
                    break;

                case DateUnits.Day:
                    timeSpam = dateTime.Date.Subtract(Start);
                    value = int.Parse(Math.Floor((double)(timeSpam.Days / Number)).ToString());
                    startOcurrence = Start.AddDays(Number * value);
                    break;
            }

            endOcurrence = startOcurrence.Add(Amount);
            //if (dateTime.Date >= startOcurrence && dateTime.Date <= endOcurrence) return true;
            if (dateTime.Date >= startOcurrence && dateTime <= endOcurrence) return true;
            else return false;
        }

        public DateTime? GetNextOcurrence(DateTime dateTime)
        {
            return dateTime;
        }

        public DateTime? GetEventEnd(DateTime dateTime)
        {
            return dateTime;
        }

        public override string ToString()
        {
            return string.Format("DUNT({0},{1},{2},{3})",
                this.Number,
                this.Unit,
                this.Start.ToString("yyyy/MM/dd h:mm:ss.fff"),
                this.Amount.ToString());
        }

        #endregion METHODS

    }
}
